home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume2 / unix / ls20.1 < prev    next >
Internet Message Format  |  1988-12-12  |  61KB

  1. Path: xanth!ames!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v02i097:  ls - unix-like directory lister v2.0
  5. Message-ID: <10673@swan.ulowell.edu>
  6. Date: 12 Dec 88 22:05:59 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 2325
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: nop@cup.portal.com
  12. Posting-number: Volume 2, Issue 97
  13. Archive-name: unix/ls20.1
  14.  
  15. Here is a new version of Justin McCormick's "ls" program.
  16. Hope the net enjoys the program -- I sure do!
  17.  
  18. #    This is a shell archive.
  19. #    Remove everything above and including the cut line.
  20. #    Then run the rest of the file through sh.
  21. #----cut here-----cut here-----cut here-----cut here----#
  22. #!/bin/sh
  23. # shar:    Shell Archiver
  24. #    Run the following text with /bin/sh to create:
  25. #    c2.a
  26. #    ls.c
  27. #    ls.doc
  28. #    ls.h
  29. #    ls.lnk
  30. #    lssup.a
  31. #    makefile
  32. # This archive created: Mon Dec 12 17:01:16 1988
  33. cat << \SHAR_EOF > c2.a
  34. *
  35. * C initial startup procedure under AmigaDOS
  36. * Use the following command line to make c.o
  37. * asm -u -iINCLUDE: c.a
  38. *
  39. * Use the following command line to make cres.o
  40. * asm -u -dRESIDENT -iINCLUDE: -ocres.o c.a
  41.  
  42.     IDNT    "c2.a"
  43.  
  44.     OPTION    L
  45.     LISTSYMS
  46.     BASEREG    B
  47.     SMALLOBJ
  48.     OPTIMON
  49.     ADDSYM
  50.     DEBUG
  51.  
  52. * --------------------------------------------------------------------- *
  53. * Macros:
  54. * --------------------------------------------------------------------- *
  55. SYS    MACRO    *
  56.     IFGT    NARG-2
  57.     FAIL    !!!
  58.     ENDC
  59.     IFEQ    NARG-2
  60.     MOVE.L    \2,a6
  61.     ENDC
  62.     JSR    _LVO\1(a6)
  63.     ENDM
  64.  
  65. XLVO    MACRO    *
  66.     XREF    _LVO\1
  67.     ENDM
  68.  
  69. * --------------------------------------------------------------------- *
  70. * Equates:
  71. * --------------------------------------------------------------------- *
  72. RESIDENT    EQU    1
  73.  
  74. MEMF_PUBLIC    EQU    $1
  75. MEMF_CLEAR    EQU    $10000
  76. MEMFLAGS    EQU    MEMF_CLEAR+MEMF_PUBLIC
  77. AbsExecBase    EQU    $0004
  78. cli_CommandName    EQU    $10
  79. pr_CLI        EQU    $AC
  80. pr_CurrentDir    EQU    $98
  81. ThisTask    EQU    $114
  82.  
  83. _LVOCloseLibrary EQU    $FE62
  84. _LVOAllocMem    EQU    $FF3A
  85. _LVOFreeMem    EQU    $FF2E
  86. _LVOSetSignal    EQU    $FECE
  87. _LVOOpenLibrary    EQU    $FDD8
  88.  
  89.     XREF    _DOSBase
  90.     XREF    _LinkerDB        ; linker defined base value
  91.     XREF    __BSSBAS        ; linker defined base of BSS
  92.     XREF    __BSSLEN        ; linker defined length of BSS
  93.  
  94.      IFD    RESIDENT
  95.     XREF    _RESLEN
  96.     XREF    _RESBASE
  97.     XREF    _NEWDATAL
  98.      ENDC
  99.     
  100.     XREF    __main            ; Name of C program to start with.
  101.     XREF    _MemCleanup        ; Free all allocated memory
  102.     XREF    ___fpinit        ; initialize floating point
  103.     XREF    ___fpterm        ; terminate floating point
  104.  
  105. * --------------------------------------------------------------------- *
  106.     SECTION    TEXT,CODE
  107. * --------------------------------------------------------------------- *
  108.     XDEF    zzstart
  109. zzstart:
  110.     move.l    a0,a2            ; save command pointer
  111.     move.l    d0,d2            ; and command length
  112.     lea    _LinkerDB,a4        ; load base register
  113.  
  114.      IFND    RESIDENT
  115.     lea    __BSSBAS,a3        ; get base of BSS
  116.     moveq    #0,d1
  117.     move.l    #__BSSLEN,d0        ; get length of BSS in longwords
  118.      bra.b    clr_lp             ; and clear for length given
  119. clr_bss move.l    d1,(a3)+
  120. clr_lp    dbf    d0,clr_bss
  121.      ENDC
  122.  
  123.      IFD    RESIDENT
  124.     movea.l    AbsExecBase,a6
  125.  
  126.     movem.l    d0-d1/a0-a2,-(sp)
  127.     sub.l    #_RESBASE,a4
  128.     move.l    #_RESLEN,d0
  129.     move.l    #MEMFLAGS,d1
  130.     SYS    AllocMem
  131.     tst.l    d0
  132.      beq.w    abort
  133.     move.l    d0,a0
  134.     move.l     d0,a2
  135.  
  136. ;a2 now has difference
  137.     move.l    d0,a1
  138.     move.l    #_NEWDATAL,d0
  139. ;copy data over
  140. cpy:    move.l    (a4)+,(a0)+
  141.     subq.l    #1,d0
  142.      bne    cpy
  143. ;a4 now points at number of relocs
  144.     move.l    (a4)+,d0
  145. reloc:     beq.b    nreloc
  146.     move.l    a1,a0
  147.     add.l    (a4)+,a0        ; a0 now has add of reloc
  148.     add.l    (a0),a2
  149.     move.l    a2,(a0) 
  150.     move.l    a1,a2            ; restore offset
  151.     subq.l    #1,d0
  152.     bra    reloc
  153.     
  154. nreloc: move.l    a1,a4            ; set up new base register
  155.     add.l    #_RESBASE,a4
  156.     movem.l    (sp)+,d0-d1/a0-a2
  157.      ENDC
  158.  
  159.     movea.l    AbsExecBase,a6
  160.     move.l    a6,_SysBase(a4)
  161.     move.l    sp,__StackPtr(a4)    ; Save stack ptr
  162.     clr.l    _WBenchMsg(a4)
  163.  
  164. * get the address of our task
  165.     move.l    ThisTask(a6),a3
  166.  
  167. *-    clear any pending signals
  168.     moveq    #0,d0
  169.     move.l    #$00003000,d1
  170.     SYS    SetSignal
  171.     
  172. * are we running as a son of Workbench?
  173.     move.l    pr_CurrentDir(a3),_curdir(a4)
  174.  
  175.     IFD    WBENCH
  176.     tst.l    pr_CLI(a3)
  177.      beq    fromWorkbench
  178.     ENDC
  179.  
  180. *=======================================================================
  181. *====== CLI Startup Code ===============================================
  182. *=======================================================================
  183. *
  184. ; Entry: d2 = command length
  185. ;     a2 = Command pointer
  186. fromCLI:
  187.     move.l    sp,d0             ; get top of stack
  188.     sub.l    4(sp),d0        ; compute bottom 
  189.     add.l    #128,d0             ; allow for parms overflow
  190.     move.l    d0,__base(a4)        ; save for stack checking
  191.  
  192. * attempt to open DOS library:
  193.     bsr.w    openDOS
  194.  
  195. * find command name:
  196.     move.l    pr_CLI(a3),a0
  197.     add.l    a0,a0             ; bcpl pointer conversion
  198.     add.l    a0,a0
  199.     move.l    cli_CommandName(a0),a1
  200.     add.l    a1,a1             ; bcpl pointer conversion
  201.     add.l    a1,a1
  202.  
  203. * collect parameters:
  204.     move.l    d2,d0            ; get command line length
  205.     moveq.l    #0,d1
  206.     move.b    (a1)+,d1
  207.     move.l    a1,__ProgramName(a4)
  208.     add.l    d1,d0            ; add length of command name
  209.     addq.l    #1,d0            ; allow for space after command 
  210.  
  211.     clr.w    -(sp)            ; set null terminator for command line
  212.     addq.l    #1,d0            ; force to even number of bytes
  213.     andi.w    #$fffe,d0        ; (round up)
  214.     sub.l    d0,sp            ; make room on stack for command line
  215.     subq.l    #2,d0
  216.     clr.w    0(sp,d0)
  217.  
  218. * copy command line onto stack
  219.     move.l    d2,d0            ; get command line length
  220.     subq.l    #1,d0
  221.     add.l    d1,d2
  222.  
  223. copy_line:
  224.     move.b    0(a2,d0.w),0(sp,d2.w)    ; copy command line to stack
  225.     subq.l    #1,d2
  226.     dbf    d0,copy_line
  227.     move.b    #' ',0(sp,d2.w)         ; add space between command and parms
  228.     subq.l    #1,d2
  229.  
  230. copy_cmd:
  231.     move.b    0(a1,d2.w),0(sp,d2.w)    ; copy command name to stack
  232.     dbf    d2,copy_cmd
  233.     move.l    sp,a1
  234.     move.l    a1,-(sp)        ; push command line address
  235.  
  236.     IFD    WBENCH
  237.     bra    main            ; call C entrypoint
  238. * --------------------------------------------------------------------- *
  239. * Workbench Startup Code
  240. * --------------------------------------------------------------------- *
  241. fromWorkbench:
  242.     move.l    TC_SPLOWER(a3),__base(a4) ; set base of stack
  243.     moveq     #127,d0
  244.     addq.l    #1,d0            ; Efficient way of getting in 128
  245.     add.l    d0,__base(a4)        ; allow for parms overflow
  246.  
  247. * open the DOS library:
  248.     bsr.w    openDOS
  249.  
  250. * we are now set up. wait for a message from our starter
  251.     lea    pr_MsgPort(a3),a0    ; our process base
  252.     SYS    WaitPort
  253.     lea    pr_MsgPort(a3),a0    ; our process base
  254.     SYS    GetMsg
  255.     move.l    d0,_WBenchMsg(a4)
  256.     move.l    d0,-(sp)
  257.  
  258.     move.l    d0,a2            ; get first argument
  259.     move.l    sm_ArgList(a2),d0
  260.      beq    do_cons
  261.     move.l    _DOSBase(a4),a6
  262.     move.l    d0,a0
  263.     move.l    wa_Lock(a0),d1
  264.     move.l    d1,_curdir(a4)
  265.     SYS    CurrentDir
  266. do_cons:
  267.     move.l    sm_ToolWindow(a2),d1    ; get the window argument
  268.      beq    do_main
  269.     move.l    #MODE_OLDFILE,d2
  270.     SYS    Open
  271.     move.l    d0,_stdin(a4)
  272.      beq    do_main
  273.     lsl.l    #2,d0
  274.     move.l    d0,a0
  275.     move.l    fh_Type(a0),pr_ConsoleTask(a3)
  276. do_main:
  277.     move.l    _WBenchMsg(a4),a0    ; get address of workbench message
  278.     move.l    a0,-(sp)        ; push argv
  279.     pea    _NULL(a4)        ; push argc
  280.     move.l    sm_ArgList(a0),a0    ; get address of arguments
  281.     move.l    wa_Name(a0),__ProgramName(a4)     ; get name of program
  282.     ENDC
  283.  
  284. * --------------------------------------------------------------------- *
  285. * Common CLI/WBENCH code
  286. * --------------------------------------------------------------------- *
  287. main:
  288.      IFD    FLOAT
  289.     jsr    ___fpinit(pc)        ; Initialize floating point
  290.      ENDC
  291.     jsr    __main(pc)        ; call C entrypoint
  292.     moveq.l    #0,d0            ; set successful status
  293.     bra.b    exit2
  294.  
  295.     XDEF    _XCEXIT
  296. _XCEXIT:
  297.     move.l    4(sp),d0        ; extract return code
  298.  
  299.     XDEF    xXCEXIT
  300. xXCEXIT:
  301. exit2:
  302.      IFD    ERRTRAPS
  303.     move.l    d0,-(sp)
  304.     move.l    __ONEXIT(a4),d0        ; exit trap function?
  305.      beq.b    exit3
  306.     move.l    d0,a0
  307.     jsr    (a0)
  308. exit3:
  309.      ENDC
  310.  
  311.      IFD    MALLOC
  312.     jsr    _MemCleanup(pc)        ; cleanup leftover memory alloc.
  313.      ENDC
  314.  
  315.     movea.l    AbsExecBase,a6
  316.     move.l    _DOSBase(a4),a1
  317.     SYS    CloseLibrary        ; close Dos library
  318.  
  319.      IFD    FLOAT
  320.     jsr    ___fpterm(pc)        ; clean up any floating point
  321.      ENDC
  322.  
  323.      IFD    WBENCH
  324. done_1c:
  325. * if we ran from CLI, skip workbench cleanup:
  326.     tst.l    _WBenchMsg(a4)
  327.      beq    exitToDOS
  328.     move.l    _stdin(a4),d1
  329.      beq.b    done_4
  330.     SYS    Close
  331. done_4:
  332. * return the startup message to our parent
  333. *     we forbid so workbench can't UnLoadSeg() us
  334. *     before we are done:
  335.     movea.l    AbsExecBase,a6
  336.     SYS    Forbid
  337.     move.l     _WBenchMsg(a4),a1
  338.     SYS    ReplyMsg
  339.      ENDC
  340.  
  341. * this rts sends us back to DOS:
  342. exitToDOS:
  343.      IFD    RESIDENT
  344.     move.l    #_RESLEN,d0
  345.     move.l     a4,a1
  346.     sub.l    #_RESBASE,a1
  347.     movea.l    AbsExecBase,a6
  348.     SYS    FreeMem
  349.      ENDC
  350.     
  351.     move.l    (sp)+,d0
  352.     movea.l    __StackPtr(a4),sp    ; restore stack ptr
  353.  
  354.     XDEF    xchkabort
  355. xchkabort:
  356.     rts
  357.  
  358.      IFD    RESIDENT
  359. abort:
  360.     movem.l    (sp)+,d0-d1/a0-a2
  361.     rts
  362.      ENDC
  363.  
  364. * --------------------------------------------------------------------- *
  365. noDOS:
  366.     moveq.l    #100,d0
  367.     bra    exit2
  368.  
  369. * --------------------------------------------------------------------- *
  370. *    Open the DOS library:
  371. * --------------------------------------------------------------------- *
  372. openDOS:
  373.     lea    DOSName(pc),a1
  374.     moveq.l #0,d0
  375.     SYS    OpenLibrary
  376.     move.l    d0,_DOSBase(a4)
  377.      beq    noDOS
  378.     rts
  379.  
  380. DOSName:    dc.b    'dos.library',0
  381.  
  382. * --------------------------------------------------------------------- *
  383.     section __MERGED,BSS
  384. * --------------------------------------------------------------------- *
  385.  
  386.     XDEF    _NULL,_SysBase,_WBenchMsg
  387.     XDEF    _curdir,__mbase,__mnext,__msize,__tsize
  388.     XDEF    __oserr,__OSERR
  389.      IFD    FLOAT
  390.     XDEF    __FPERR
  391.     XDEF    __SIGFPE
  392.      ENDC
  393.      IFD    ERRTRAPS
  394.     XDEF    __ONERR,__ONEXIT,__ONBREAK
  395.     XDEF    __SIGINT
  396.      ENDC
  397.     XDEF    _stdin
  398.     XDEF    __ProgramName,__StackPtr,__base
  399.  
  400. * --------------------------------------------------------------------- *
  401. _NULL        ds.l    1         ; Huh?
  402. __base        ds.l    1         ; base of stack
  403. __mbase        ds.l    1         ; base of memory pool
  404. __mnext        ds.l    1         ; next available memory location
  405. __msize        ds.l    1         ; size of memory pool
  406. __tsize        ds.l    1         ; total size?
  407. __oserr
  408. __OSERR        ds.l    1
  409.      IFD    FLOAT
  410. __FPERR        ds.l    1
  411. __SIGFPE    ds.l    1
  412.      ENDC
  413.      IFD    ERRTRAPS
  414. __SIGINT    ds.l    1
  415. __ONERR        ds.l    1
  416. __ONEXIT    ds.l    1
  417. __ONBREAK    ds.l    1
  418.      ENDC
  419. _curdir        ds.l    1
  420. _SysBase    ds.l    1
  421. _WBenchMsg    ds.l    1
  422. __StackPtr    ds.l    1
  423. _stdin        ds.l    1
  424. __ProgramName    ds.l    1
  425.  
  426. * --------------------------------------------------------------------- *
  427.     END
  428. * --------------------------------------------------------------------- *
  429. SHAR_EOF
  430. cat << \SHAR_EOF > ls.c
  431. /* --------------------------------------------------------------------- *
  432.   LS.C -- an "improved" directory listing utility to replace the
  433.   AmigaDOS DIR and LIST commands.
  434.  
  435.   V1.0   August 1986 Written from scratch by Justin V. McCormick.
  436.   V2.0 November 1988 Revised for Lattice 5.0 and made 1.3 compatible.
  437.  
  438. Notice:
  439.  
  440.   This program is placed in the public domain with the understanding
  441. that the author makes no claims or guarantees with regard to its
  442. suitability for any given application, and that the author assumes no
  443. responsibility for damages incurred by its usage.  Any resemblance
  444. of this program to any other program, either living or dead, is
  445. purely coincidental.
  446.  
  447.   Feel free to steal this code and make millions of dollars from its sale
  448. or commercial use, but please give credit where credit is due.
  449.  
  450. Synopsis:
  451.  
  452.   Features adaptive columnar listing, versatile sort options,
  453. UNIX-style pattern matching, recursive subdirectory listing, etc!
  454.  
  455. Usage:
  456.     ls [options] [path1] [path2] ...
  457.  
  458. Options:
  459.     -?    Help!
  460.     -c    Show file comment info, -c implies -l
  461.     -d    Show directory names only
  462.     -f    Show filenames only
  463.     -l      Long verbose listing showing filesizes and dates
  464.     -n    No sort, just spit them out in the order ExNext() returns
  465.     -r    Reverse sort direction
  466.     -s    Sorted by size smallest to largest
  467.     -t    Sorted by date oldest to newest
  468.     -R    Recursive descent of subdirectories
  469.  
  470.   All arguments are optional.  Default is to give short columnar listing,
  471. sorted alphabetically, using the current directory.  Alphabetizing is case
  472. insensitive.
  473.  
  474.   Patterns may be matched in the given names, using the UNIX-style '*'
  475. to wildcard any number of characters, and '?' to wildcard a single
  476. character.  If you need to specify a pathname with spaces in it like
  477. "Wombat Soup", you need to put quotes around it.  LS can process up to 30
  478. separate pathname patterns in one command line.
  479.  
  480. Bugs:
  481.  
  482.   Redirecting the shortlist output to PRT: gives undesirable results,
  483. since I am using relative cursor positioning commands to format the
  484. screen output.  I thought about using an array to store a virtual
  485. screen, but my primary goals were to keep the size down and display
  486. speed at a maxiumum.  Also, LS cannot pattern match devices (like "dh*:")
  487. or support multiple levels of pattern matching (like "dh0:?/L*.info").
  488. This would involve another level of recursion and groking the Device List.
  489.  
  490. Changes From 1.0 to 2.0:
  491.  
  492.  o Source code prototyped, linted, traced, optimized, tweaked, etc.
  493.  o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
  494.  o High-volume routines recoded in assembly (lssup.a).
  495.  o Now handles multiple paths/files on a command line, up to 30.
  496.  o New sort flags, including no sort.
  497.  o Enhanced wildcards, understands complex *.?*.* expressions now.
  498.  o More efficient ExNext() performance, less ram used for recursion.
  499.  o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
  500.  o Command line parser handles quoted pathnames now (LC 5.0 benefit).
  501.  o Short listing finally auto-adjusts to new console window sizes!
  502.  o Pen color escape codes bypassed when redirecting long output.
  503.  o Sorting by size or date is also subsorted alphabetically now.
  504.  o Long listing shows new 1.3 file attributes, plus comment indicator.
  505.  o File dates are now in international format, YY-MM-DD.
  506.  o Fixed listings with files datestamped after 99-12-31 (overflow).
  507.  o Fixed listings with files datestamped before 78-01-01 (time < 0).
  508.  
  509. * --------------------------------------------------------------------- */
  510.  
  511. #include "ls.h"
  512.  
  513. /* Structure used to hold file info in a linked list */
  514. struct FibEntry
  515. {
  516.   struct FibEntry *NextFib;
  517.   struct FibEntry *LastFib;
  518.   struct FileInfoBlock *Fibp;
  519. };
  520.  
  521. /* Externs from lssup.a */
  522. /*lint +fva2 */
  523. extern int __stdargs asprintf (char *, char *,...);
  524. /*lint -fva2 */
  525.  
  526. extern __asm void SortFibs (R_D0 long, R_D1 long, R_A0 struct FibEntry *);
  527. extern __stdargs long iswild (char *);
  528. extern __stdargs long wildmatch (char *, char *);
  529. extern __stdargs char *FibFileDate (struct DateStamp *);
  530. extern __stdargs void GetWinBounds(long *, long *);
  531.  
  532. /* Local CODE */
  533. struct FibEntry *GetDir (BPTR, struct FileInfoBlock *);
  534. struct FibEntry *AllocFib (void);
  535. void CleanUp (char *, long);
  536. void ColorPen2 (void);
  537. void CursorOff (void);
  538. void CursorOn (void);
  539. void DirIt (BPTR, char *);
  540. void FreeAllFibs (struct FibEntry *);
  541. void FreeFib (struct FibEntry *);
  542. void LListDir (struct FibEntry *);
  543. void LListEntry (struct FileInfoBlock *);
  544. void LongList (long *, long *, struct FibEntry *);
  545. void main (int, char **);
  546. void PagePrompt (long);
  547. void ResetPen (void);
  548. void SListDir (struct FibEntry *);
  549. void TestBreak (void);
  550. void Usage (void);
  551. void WCHR (char *);
  552. void WSTR (char *);
  553.  
  554. BPTR Out = 0L;
  555. BPTR In = 0L;
  556. BPTR lockp = 0L;
  557. struct FileInfoBlock *GFibp = 0L;
  558.  
  559. long BREAKFLAG = 0;
  560. long CONSOLE = 0;
  561. long dircount = 0;
  562. long DIRFILEFLAG = 0;
  563. long errstat = 0;
  564. long filecount = 0;
  565. long LISTALL = 0;
  566. long LONGLIST = 0;
  567. long maxnamlen = 0;
  568. long NOSORTFLAG = 0;
  569. long NOTEFLAG = 0;
  570. long PATHNAMED = 0;
  571. long REVFLAG = 0;
  572. long sortkey = 0;
  573. long WILDCARD = 0;
  574. long CurWinRows = 20L;
  575. long CurWinCols = 77L;
  576.  
  577. char filename[160];
  578. char pattern[160];
  579. char workstr[160];
  580.  
  581. static char Author[] = "\23333mLS 2.0\2330m by Justin V. McCormick 1988";
  582. static char usage[] = "\nUsage: ls [-cdflnrstR] [path1] [path2] ...\n";
  583.  
  584. /* -------------------------------------------------------------------- */
  585. void main (argc, argv)
  586.   int argc;
  587.   char **argv;
  588. {
  589.   struct Process *procp;
  590.   long cnt, i;
  591.  
  592.   if (argc == 0)            /* son of Workbench -- no go! */
  593.     exit (0);
  594.  
  595. /* Grab FileHandles for input and output to console (or redirection file) */
  596.   In = Input ();
  597.   Out = Output ();
  598.   CONSOLE = (IsInteractive (Out) == 0L) ? 0L : 1L; /* Is this console output? */
  599.  
  600. /* Allocate a global FileInfoBlock for ExNext() */
  601.   if ( (GFibp = (struct FileInfoBlock *)AllocMem((long)sizeof(struct FileInfoBlock), MEMF_PUBLIC | MEMF_CLEAR)) == 0L)
  602.     CleanUp("No RAM?", 103L);
  603.  
  604. /* Parse command line arguments <ugh> */
  605.   cnt = 1;
  606.   if (argc >= 2)
  607.   {
  608.     if (argv[1][0] == '-')
  609.     {
  610.       cnt++;
  611.       for (i = strlen(argv[1]) - 1; i > 0; i--)
  612.       {
  613.         switch (argv[1][i])
  614.         {
  615.       case 'c': 
  616.         LONGLIST = 1;
  617.         NOTEFLAG = 1;
  618.         break;
  619.           case 'd':
  620.             DIRFILEFLAG |= 1;
  621.             break;
  622.           case 'f':
  623.             DIRFILEFLAG |= 2;
  624.             break;
  625.       case 'l': 
  626.         LONGLIST = 1;
  627.         break;
  628.       case 'n':
  629.         NOSORTFLAG = 1;
  630.         break;
  631.       case 'r':
  632.         REVFLAG = 1;
  633.         break;
  634.       case 's':
  635.         sortkey = 1;
  636.         break;
  637.       case 't': 
  638.         sortkey = 2;
  639.         break;
  640.       case 'R': 
  641.         LISTALL = 1;
  642.         break;
  643.       case '?': 
  644.         Usage ();
  645.         break;
  646.       default: 
  647.         (void) asprintf (workstr, "Unknown option \'%c\'\n", argv[1][i]);
  648.         WSTR (workstr);
  649.         Usage ();
  650.         break;
  651.     }
  652.       }
  653.     }
  654.   }
  655.  
  656. /* Clean up the state flags */
  657.   if ( (argc - cnt) > 1)
  658.     LISTALL |= 2;
  659.  
  660.   if (DIRFILEFLAG == 0)
  661.     DIRFILEFLAG = 3;
  662.  
  663. /* Loop through the remaining args now */
  664.   do
  665.   {
  666.     PATHNAMED = 0;
  667.     if (cnt < argc)
  668.     {
  669.       (void) stpcpy (pattern, argv[cnt]);
  670.       PATHNAMED = 1;
  671.     }
  672.  
  673.     if (PATHNAMED)            /* If user specified a pathname    */
  674.     {
  675.       WILDCARD = iswild (pattern);     /* check for wildcards         */
  676.  
  677.       if (WILDCARD)            /* If wildcards, separate    */
  678.       {                    /* pattern from pathname    */
  679.         for (i = (strlen (pattern) - 1); i >= 0; i--)
  680.         {
  681.           if (pattern[i] == '/' || pattern[i] == ':')
  682.           {
  683.         (void) strncpy (filename, pattern, (UWORD) (i + 1));
  684.         filename[i + 2] = (BYTE) 0;
  685.         (void) stpcpy (workstr, &pattern[i + 1]);
  686.         (void) stpcpy (pattern, workstr);
  687.         break;
  688.           }
  689.         }
  690. /* Disallow wildcards in pathname */
  691.         if (iswild (filename))
  692.           CleanUp ("Sorry, can't pattern match paths", 5L);
  693.       }
  694.       else                /* No wildcards, use filename as is */
  695.       {
  696.         (void) stpcpy (filename, pattern);
  697.       }
  698.  
  699. /* If the user specified a pathname, try to grab a FileLock on it */
  700. /* Discard trailing slash if silly Joe User put one there */
  701.       if (filename[1] != 0 && filename[strlen (filename) - 1] == '/')
  702.         filename[strlen (filename) - 1] = (BYTE) 0;
  703.  
  704.       lockp = Lock (filename, ACCESS_READ);
  705.  
  706.       if (!lockp)            /* Can't Lock it!         */
  707.       {
  708.         errstat = IoErr ();
  709.         (void) strcat (filename, " not found");
  710.         CleanUp (filename, (long)errstat);
  711.       }
  712.     }
  713.     else
  714.     {
  715. /*
  716.  * If no filename was specified, steal Lock on current directory from
  717.  * CLI process task info.  We durn well better get something useful back;
  718.  * we don't do any error checking on the stolen Lock.
  719.  */
  720.       procp = (struct Process *) FindTask (0L);
  721.       lockp = procp->pr_CurrentDir;
  722.       filename[0] = 0;            /* Tell DirIt() to use current dir */
  723.     }
  724.  
  725. /* Get the directory for this path, display it */
  726.     DirIt (lockp, filename);
  727.  
  728. /* Release the lock, bump our arg counter */
  729.     if (lockp != 0 && PATHNAMED != 0)
  730.       UnLock (lockp);
  731.     lockp = 0L;
  732.     cnt++;
  733.  
  734.     if (cnt < argc)
  735.       WCHR("\n");
  736.   } while (cnt < argc && BREAKFLAG == 0);
  737.  
  738.   CleanUp ("", 0L);
  739. }
  740.  
  741. /* -------------------------------------------------------------------- */
  742. /* Deallocate and close everything                     */
  743. /* -------------------------------------------------------------------- */
  744. void CleanUp (exit_msg, exit_status)
  745.   char *exit_msg;
  746.   long exit_status;
  747. {
  748.   if (lockp && PATHNAMED)
  749.     UnLock (lockp);
  750.  
  751.   if (GFibp != 0L)
  752.     FreeMem(GFibp, (long)sizeof(struct FileInfoBlock));
  753.  
  754.   if (exit_status)
  755.   {
  756.     (void) asprintf (workstr, "ls: %s, Error #%ld\n", exit_msg, exit_status);
  757.     WSTR (workstr);
  758.   }
  759.   exit ((int) exit_status);
  760. }
  761.  
  762. /* -------------------------------------------------------------------- */
  763. void DirIt (lockp, dirname)
  764.   BPTR lockp;
  765.   char *dirname;
  766. {
  767.   BPTR  tlockp;
  768.   struct FibEntry *fibheadp;
  769.   struct FibEntry *tfibp;
  770.   char *subdir;
  771.   long strsize;
  772.  
  773. /* Try to fill FileInfoBlock, bomb if not readable for some reason */
  774.   if (!Examine (lockp, GFibp))
  775.   {
  776.     (void) asprintf (workstr, "Can't examine file or directory\n");
  777.     WSTR (workstr);
  778.     return;
  779.   }
  780.  
  781. /* Put directory header if this is a recursive listing */
  782.   if (dirname[0] && LISTALL > 0)
  783.   {
  784.     if (CONSOLE != 0)
  785.      (void)asprintf(workstr, "\23330;41m %s \2330m\n", dirname);
  786.     else
  787.      (void)asprintf(workstr, "%s\n", dirname);
  788.     WSTR(workstr);
  789.   }
  790.  
  791. /* If this is a single file list it verbosely */
  792.   if (GFibp->fib_EntryType < 0 && (DIRFILEFLAG & 2) != 0)
  793.   {
  794.     LListEntry (GFibp);
  795.   }
  796.   else
  797.   {
  798. /* Otherwise do a directory     */
  799. /* Allocate and initialize a FibEntry head node */
  800.     if ( (fibheadp = GetDir (lockp, GFibp)) != 0L && BREAKFLAG == 0)
  801.     {
  802.       if (NOSORTFLAG == 0)
  803.         SortFibs ((long)sortkey, REVFLAG, fibheadp);
  804.  
  805.       if (LONGLIST == 0)
  806.     SListDir (fibheadp);
  807.       else
  808.     LListDir (fibheadp);
  809.  
  810.       if ( (LISTALL & 1) != 0)
  811.       {
  812.         tfibp = fibheadp;
  813.     do
  814.     {
  815.       if (tfibp->Fibp->fib_EntryType > 0)
  816.       {
  817.         strsize = (strlen (dirname) + strlen (tfibp->Fibp->fib_FileName) + 2);
  818.         subdir = (char *) AllocMem ((LONG)strsize, 0L);
  819.         if (strlen (dirname) != 0)
  820.         {
  821.           (void) stpcpy (subdir, dirname);
  822.           if (dirname[strlen (dirname) - 1] != ':')
  823.         (void) strcat (subdir, "/");
  824.         }
  825.         (void) strcat (subdir, tfibp->Fibp->fib_FileName);
  826.         tlockp = Lock (subdir, ACCESS_READ);
  827.         if (tlockp == 0L)
  828.         {
  829.           WSTR (subdir);
  830.           WSTR (" -- can't lock it!\n");
  831.           break;
  832.         }
  833.         else
  834.         {
  835.           WCHR ("\n");        /* Put a blank line between directories */
  836.           DirIt (tlockp, subdir);
  837.           UnLock (tlockp);
  838.           FreeMem (subdir, (LONG)strsize);
  839.         }
  840.       }
  841.       tfibp = tfibp->NextFib;
  842.     } while (tfibp != fibheadp && BREAKFLAG == 0);
  843.       }
  844.     }
  845. /* Clean up */
  846.     FreeAllFibs (fibheadp);
  847.   }
  848. }
  849.  
  850. /* -------------------------------------------------------------------- */
  851. /* Allocate and fill a linked list of FileInfoBlocks             */
  852. /* -------------------------------------------------------------------- */
  853. struct FibEntry *GetDir (lockp, fibp)
  854.   BPTR lockp;
  855.   struct FileInfoBlock *fibp;
  856. {
  857.   long nextstat;
  858.   long tempnamlen;
  859.   struct FibEntry *tfibp;
  860.   struct FibEntry *headfib;
  861.  
  862.   maxnamlen = dircount = filecount = 0L;
  863.   headfib = 0L;
  864.  
  865.   do
  866.   {
  867.     TestBreak();
  868.     if (BREAKFLAG != 0)
  869.       return(headfib);
  870.     nextstat = ExNext (lockp, fibp);
  871.  
  872.     if (nextstat != 0)    /* We got something */
  873.     {
  874. /* See if it matches our pattern */
  875.       if (!WILDCARD || wildmatch (fibp->fib_FileName, pattern))
  876.       {
  877.  
  878. /* Bump count of files or directories */
  879.     if (fibp->fib_EntryType > 0)
  880.         {
  881.           if ((DIRFILEFLAG & 1) != 0)
  882.             dircount++;
  883.           else
  884.             goto ALLOCFIB;
  885.         }
  886.         else
  887.         {
  888.           if ((DIRFILEFLAG & 2) != 0)
  889.             filecount++;
  890.           else
  891.             continue;
  892.         }
  893.  
  894. /* See if this is the longest filename for later use in listing */
  895.     tempnamlen = strlen (fibp->fib_FileName);
  896.     if (tempnamlen > maxnamlen)
  897.       maxnamlen = tempnamlen;
  898.  
  899.         ALLOCFIB:
  900. /* Allocate another FibEntry to put the info in */
  901.         if (headfib == 0L)
  902.         {
  903.           headfib = AllocFib();
  904.           if (headfib == 0L)
  905.             return(headfib);
  906.           headfib->NextFib = headfib;
  907.           headfib->LastFib = headfib;
  908.           *(headfib->Fibp) = *(fibp);
  909.           tfibp = headfib;
  910.         }
  911.         else
  912.         {
  913.       tfibp->NextFib = AllocFib ();
  914.       if (tfibp->NextFib == 0L)
  915.         return(0L);
  916.  
  917. /* Copy FIB contents to next entry for ExNext to work with */
  918.       *(tfibp->NextFib->Fibp) = *(fibp);
  919.  
  920. /* Link it into the list */
  921.       tfibp->NextFib->LastFib = tfibp;
  922.       tfibp = tfibp->NextFib;
  923.       tfibp->NextFib = headfib;
  924.     }
  925.       }
  926.     }
  927.   } while (nextstat);
  928.  
  929. /* Return TRUE if entries found, else print message and return FALSE */
  930.   if ( (dircount + filecount) != 0)
  931.   {
  932.     return (headfib);
  933.   }
  934.   else
  935.   {
  936.     if (WILDCARD == 0 && DIRFILEFLAG == 3)
  937.       WSTR ("Volume or directory is empty.\n");
  938.     else
  939.       WSTR ("No match.\n");
  940.     return (0L);
  941.   }
  942. }
  943.  
  944. /* -------------------------------------------------------------------- */
  945. /* List a FibEntry list in a compact fashion                 */
  946. /* -------------------------------------------------------------------- */
  947. void SListDir (fibheadp)
  948.   struct FibEntry *fibheadp;
  949. {
  950.   struct FibEntry *tfibp;
  951.   long tabsize;
  952.   long maxtab;
  953.   long totrows;
  954.   long maxwinrow;
  955.   long colcnt;
  956.   long rowcnt;
  957.   long tabcnt;
  958.   long pagecnt = 1;
  959.  
  960.   CursorOff ();        /* Turn the cursor off since it will blink anyway */
  961.   GetWinBounds(&CurWinCols, &CurWinRows);
  962.  
  963.   tfibp = fibheadp;
  964.   tabcnt = rowcnt = colcnt = 0;
  965.  
  966.   tabsize = maxnamlen + 2;
  967.   if (CurWinCols < tabsize)
  968.     maxtab = 1;
  969.   else
  970.     maxtab = CurWinCols / tabsize;
  971.   maxwinrow = CurWinRows - 3;
  972.   if (maxwinrow <= 0)
  973.     maxwinrow = 1;
  974.   totrows = (dircount + filecount) / maxtab;
  975.   if ((dircount + filecount) % maxtab != 0)
  976.     totrows++;
  977.  
  978.   do
  979.   {
  980.     if (tfibp->Fibp->fib_EntryType > 0)
  981.     {
  982.       if ( (DIRFILEFLAG & 1) == 0)
  983.         goto GETNEXTFIB;
  984.       else
  985.         ColorPen2 ();
  986.     }
  987.     if (tabcnt)
  988.       (void) asprintf (workstr, "\233%ldC", (long)tabcnt);
  989.     else
  990.       workstr[0] = (BYTE) 0;
  991.     (void) strcat (workstr, tfibp->Fibp->fib_FileName);
  992.     (void) strcat (workstr, "\n");
  993.     WSTR (workstr);
  994.     if (tfibp->Fibp->fib_EntryType > 0)
  995.       ResetPen ();
  996.     rowcnt++;
  997.  
  998.     if (rowcnt == maxwinrow || rowcnt == totrows)
  999.     {
  1000.       colcnt++;                /* Start a new column         */
  1001. /* Check to see if we have used the last column up and are about to run
  1002.  * off the screen entirely.  If so, give the user a chance to read it first.
  1003.  */
  1004.       if (colcnt == maxtab && rowcnt == maxwinrow && CONSOLE != 0)
  1005.       {
  1006.         if (maxwinrow > 1)
  1007.         {
  1008.          ++pagecnt;            /* Advance page number count    */
  1009.      PagePrompt (pagecnt);        /* Print it, wait for user    */
  1010.     }
  1011.     totrows -= maxwinrow;
  1012.     colcnt = tabcnt = rowcnt = 0L;
  1013.       }
  1014.       else    /* Just move over one row and back up to the top */
  1015.       {
  1016.     (void) asprintf (workstr, "\233%ldA", (long)rowcnt);
  1017.     WSTR (workstr);
  1018.     tabcnt += tabsize;
  1019.     rowcnt = 0L;
  1020.       }
  1021.     }
  1022.     GETNEXTFIB:
  1023.     tfibp = tfibp->NextFib;
  1024.   } while (tfibp != fibheadp);
  1025.  
  1026.   if (totrows - rowcnt > 0)        /* Cursor down till level with     */
  1027.   {                    /* lowest line on screen     */
  1028.     (void) asprintf (workstr, "\233%ldE", (long)(totrows - rowcnt));
  1029.     WSTR (workstr);
  1030.   }
  1031.   CursorOn ();
  1032. }
  1033.  
  1034. /* -------------------------------------------------------------------- */
  1035. /* Reset character color to default Pen1 colors             */
  1036. /* -------------------------------------------------------------------- */
  1037. void ResetPen ()
  1038. {
  1039.   if (CONSOLE != 0)
  1040.     WSTR ("\2330m");
  1041. }
  1042.  
  1043. /* -------------------------------------------------------------------- */
  1044. /* Turn the cursor on                            */
  1045. /* -------------------------------------------------------------------- */
  1046. void CursorOn ()
  1047. {
  1048.   if (CONSOLE != 0)
  1049.     WSTR ("\233 p");
  1050. }
  1051.  
  1052. /* -------------------------------------------------------------------- */
  1053. /* Turn the cursor off for faster text output                 */
  1054. /* -------------------------------------------------------------------- */
  1055. void CursorOff ()
  1056. {
  1057.   if (CONSOLE != 0)
  1058.     WSTR ("\2330 p");
  1059. }
  1060.  
  1061. /* -------------------------------------------------------------------- */
  1062. /* Make Pen2 <usually yellow/orange> the current charcter color     */
  1063. /* -------------------------------------------------------------------- */
  1064. void ColorPen2 ()
  1065. {
  1066.   if (CONSOLE != 0)
  1067.     WSTR ("\23333m");
  1068. }
  1069.  
  1070. /* -------------------------------------------------------------------- */
  1071. /* Prompt the user to hit return, wait till return is hit         */
  1072. /* -------------------------------------------------------------------- */
  1073. void PagePrompt (page)
  1074.   long page;
  1075. {
  1076.   WSTR ("\2337m -- MORE -- Press Return: \2330m");
  1077.   (void) Read (In, workstr, 1L);
  1078.   (void) asprintf (workstr, "\233F\233K\2334;33mPage %ld:\2330m\n", (long)page);
  1079.   WSTR (workstr);
  1080. }
  1081.  
  1082. /* -------------------------------------------------------------------- */
  1083. /* List a directory in a verbose informative manner             */
  1084. /* -------------------------------------------------------------------- */
  1085. void LListDir (fibheadp)
  1086.   struct FibEntry *fibheadp;
  1087. {
  1088.   long totblocks = 0L;
  1089.   long totbytes = 0L;
  1090.  
  1091.   CursorOff ();
  1092.   LongList (&totblocks, &totbytes, fibheadp);
  1093.  
  1094.   (void) asprintf (workstr, "Dirs:%-3ld Files:%-4ld Blocks:%-5ld Bytes:%-8ld\n",
  1095.       (long)dircount, (long)filecount, totblocks, totbytes);
  1096.   WSTR (workstr);
  1097.   CursorOn ();
  1098. }
  1099.  
  1100. /* -------------------------------------------------------------------- */
  1101. void LongList (totblocks, totbytes, fibheadp)
  1102.   long *totblocks, *totbytes;
  1103.   struct FibEntry *fibheadp;
  1104. {
  1105.   struct FibEntry *tfibp;
  1106.  
  1107.   tfibp = fibheadp;
  1108.   do
  1109.   {
  1110.     LListEntry (tfibp->Fibp);
  1111.     if (tfibp->Fibp->fib_EntryType < 0)
  1112.     {
  1113.       *totblocks += tfibp->Fibp->fib_NumBlocks;
  1114.       *totbytes += tfibp->Fibp->fib_Size;
  1115.     }
  1116.     tfibp = tfibp->NextFib;
  1117.   } while (tfibp != fibheadp);
  1118. }
  1119.  
  1120. /* -------------------------------------------------------------------- */
  1121. /* Verbosely list a particular FibEntry                 */
  1122. /* -------------------------------------------------------------------- */
  1123. void LListEntry (fib)
  1124.   struct FileInfoBlock *fib;
  1125. {
  1126.   long i, pmodes;
  1127.   char *cp1;
  1128.   char entry[160];
  1129.  
  1130.   pmodes = fib->fib_Protection & 0xff;
  1131.   cp1 = stpcpy (entry, "chsparwed ");
  1132.   for (i = 0; i < 4; i++)
  1133.   {
  1134.     if ((pmodes & (1 << i)) != 0)
  1135.       entry[8 - i] = '-';
  1136.   }
  1137.   for (; i < 8; i++)
  1138.   {
  1139.     if ((pmodes & (1 << i)) == 0)
  1140.       entry[8 - i] = '-';
  1141.   }
  1142.   if (fib->fib_Comment[0] == 0)
  1143.     entry[0] = '-';
  1144.  
  1145.   cp1 = stpcpy (cp1, FibFileDate (&fib->fib_Date));
  1146.   if (fib->fib_EntryType > 0)
  1147.   {
  1148.     if ( (DIRFILEFLAG & 1) == 0)
  1149.       return;
  1150.  
  1151.     if (CONSOLE)
  1152.       cp1 = stpcpy (cp1, "\23333m");
  1153.     cp1 = stpcpy (cp1, "   Directory   ");
  1154.     if (CONSOLE)
  1155.       cp1 = strcat (cp1, "\2330m");
  1156.   }
  1157.   else
  1158.     (void) asprintf (&entry[27], " %4ld %8ld ", fib->fib_NumBlocks, fib->fib_Size);
  1159.   (void)strcat(entry, fib->fib_FileName);
  1160.   (void)strcat(cp1, "\n");
  1161.   WSTR (entry);
  1162.  
  1163.   if (NOTEFLAG != 0 && fib->fib_Comment[0] != 0)
  1164.   {
  1165.     if (CONSOLE)
  1166.       (void)asprintf(cp1, "\23333m/* %s */\2330m\n", fib->fib_Comment);
  1167.     else
  1168.       (void)asprintf(cp1, "/* %s */\n", fib->fib_Comment);
  1169.     WSTR (workstr);
  1170.   }
  1171. }
  1172.  
  1173. #ifdef NOASM
  1174. /* -------------------------------------------------------------------- */
  1175. /* Calculate date based on DateStamp structure, return a string pointer */
  1176. /* -------------------------------------------------------------------- */
  1177. char *CalcDate (fib)
  1178.   struct FileInfoBlock *fib;
  1179. {
  1180.   static long days[12] =
  1181.   {
  1182.     31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  1183.   };
  1184.  
  1185.   static char *months[12] =
  1186.   {
  1187.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1188.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  1189.   };
  1190.   char datestr[25];
  1191.   long i, mdays;
  1192.   long day, hour;
  1193.   long minute, sec;
  1194.   long ldays;
  1195.   long year;
  1196.  
  1197.   ldays = 1461L;
  1198.   year = 78L;
  1199.   day = fib->fib_Date.ds_Days;
  1200.   minute = fib->fib_Date.ds_Minute;
  1201.   sec = fib->fib_Date.ds_Tick / 50L;
  1202.   year += (day / ldays) * 4L;
  1203.   day %= ldays;
  1204.  
  1205.   while (day)
  1206.   {
  1207.     mdays = 365;
  1208.     if ((year & 3) == 0)
  1209.       mdays++;
  1210.     if (day < mdays)
  1211.       break;
  1212.     day -= mdays;
  1213.     year++;
  1214.   }
  1215.  
  1216.   for (i = 0L, day++; i < 12; i++)
  1217.   {
  1218.     mdays = days[i];
  1219.     if (i == 1 && (year & 3) == 0)
  1220.       mdays++;
  1221.     if (day <= mdays)
  1222.       break;
  1223.     day -= mdays;
  1224.   }
  1225.   hour = minute / 60;
  1226.   minute -= hour * 60;
  1227.   (void) asprintf (datestr, "%02ld-%s-%02ld %02ld:%02ld:%02ld ", day, (long)months[i], year, hour, minute, sec);
  1228.   return (datestr);
  1229. }
  1230. #endif
  1231.  
  1232. /* -------------------------------------------------------------------- */
  1233. /* Use AmigaDos to put a string on the stdout                 */
  1234. /* -------------------------------------------------------------------- */
  1235. void WSTR (tstring)
  1236.   char *tstring;
  1237. {
  1238.   (void) Write (Out, tstring, (long) strlen (tstring));
  1239. }
  1240.  
  1241. /* -------------------------------------------------------------------- */
  1242. /* Use AmigaDos to put a character on the stdout             */
  1243. /* -------------------------------------------------------------------- */
  1244. void WCHR (ch)
  1245.   char *ch;
  1246. {
  1247.   (void) Write (Out, ch, 1L);
  1248. }
  1249.  
  1250. /* -------------------------------------------------------------------- */
  1251. /* Check to see if the user hit ^C                     */
  1252. /* -------------------------------------------------------------------- */
  1253. void TestBreak ()
  1254. {
  1255.   unsigned long oldsig;
  1256.  
  1257.   oldsig = SetSignal (0L, SIGBREAKF_CTRL_C);
  1258.   if ( (oldsig & SIGBREAKF_CTRL_C) != 0L)
  1259.   {
  1260.     WSTR ("\2330m\233 p**BREAK\n");
  1261.     BREAKFLAG = 1;
  1262.   }
  1263. }
  1264.  
  1265. /* -------------------------------------------------------------------- */
  1266. /* Explain how to use                             */
  1267. /* -------------------------------------------------------------------- */
  1268. void Usage ()
  1269. {
  1270.   WSTR (Author);
  1271.   WSTR (usage);
  1272.   WSTR (" c > Show comments\n");
  1273.   WSTR (" d > Dirs only\n");
  1274.   WSTR (" f > Files only\n");
  1275.   WSTR (" l > Long listing\n");
  1276.   WSTR (" n > No sort\n");
  1277.   WSTR (" r > Reverse sort\n");
  1278.   WSTR (" s > Sort by size\n");
  1279.   WSTR (" t > Sort by date\n");
  1280.   WSTR (" R > Recursive listing\n");
  1281.   CleanUp ("", 0L);
  1282. }
  1283.  
  1284. /* -------------------------------------------------------------------- */
  1285. /* Allocate a FibEntry structure and associated FileInfoBlock         */
  1286. /* -------------------------------------------------------------------- */
  1287. struct FibEntry *AllocFib ()
  1288. {
  1289.   struct FibEntry *tfibp;
  1290.  
  1291.   tfibp = (struct FibEntry *) AllocMem \
  1292. ((long)( sizeof (struct FibEntry) + sizeof(struct FileInfoBlock) ), 0L);
  1293.   if (tfibp != 0L)
  1294.   {
  1295.     tfibp->Fibp = (struct FileInfoBlock *)((ULONG)tfibp + sizeof(struct FibEntry));
  1296.   }
  1297.   else
  1298.     BREAKFLAG = 1;
  1299.   return (tfibp);
  1300. }
  1301.  
  1302. /* -------------------------------------------------------------------- */
  1303. /* Free up memory allocated to a linked list of FibEntrys         */
  1304. /* -------------------------------------------------------------------- */
  1305. void FreeAllFibs (fibheadp)
  1306.   struct FibEntry *fibheadp;
  1307. {
  1308.   struct FibEntry *fibp;
  1309.   struct FibEntry *tfibp;
  1310.  
  1311.   if (fibheadp != 0)
  1312.   {
  1313.     fibp = fibheadp;
  1314.     do
  1315.     {
  1316.       tfibp = fibp->NextFib;
  1317.       FreeFib (fibp);
  1318.       fibp = tfibp;
  1319.     } while (tfibp != fibheadp);
  1320.   }
  1321. }
  1322.  
  1323. /* -------------------------------------------------------------------- */
  1324. /* Deallocate a single FibEntry structure                 */
  1325. /* -------------------------------------------------------------------- */
  1326. void FreeFib (fibp)
  1327.   struct FibEntry *fibp;
  1328. {
  1329.   if (fibp != 0L)
  1330.     FreeMem (fibp, (long)(sizeof (struct FibEntry) + sizeof(struct FileInfoBlock)));
  1331. }
  1332.  
  1333. SHAR_EOF
  1334. cat << \SHAR_EOF > ls.doc
  1335.  
  1336.   LS.C -- an "improved" directory listing utility to replace the
  1337.   AmigaDOS DIR and LIST commands.
  1338.  
  1339.   V1.0   August 1986 Written from scratch by Justin V. McCormick.
  1340.   V2.0 November 1988 Revised for Lattice 5.0 and made 1.3 compatible.
  1341.  
  1342. Notice:
  1343.  
  1344.   This program is placed in the public domain with the understanding
  1345. that the author makes no claims or guarantees with regard to its
  1346. suitability for any given application, and that the author assumes no
  1347. responsibility for damages incurred by its usage.  Any resemblance
  1348. of this program to any other program, either living or dead, is
  1349. purely coincidental.
  1350.  
  1351.   Feel free to steal this code and make millions of dollars from its sale
  1352. or commercial use, but please give credit where credit is due.
  1353.  
  1354. Synopsis:
  1355.  
  1356.   Features adaptive columnar listing, versatile sort options,
  1357. UNIX-style pattern matching, recursive subdirectory listing, etc!
  1358.  
  1359. Usage:
  1360.     ls [options] [path1] [path2] ...
  1361.  
  1362. Options:
  1363.     -?    Help!
  1364.     -c    Show file comment info, -c implies -l
  1365.     -d    Show directory names only
  1366.     -f    Show filenames only
  1367.     -l      Long verbose listing showing filesizes and dates
  1368.     -n    No sort, just spit them out in the order ExNext() returns
  1369.     -r    Reverse sort direction
  1370.     -s    Sorted by size smallest to largest
  1371.     -t    Sorted by date oldest to newest
  1372.     -R    Recursive descent of subdirectories
  1373.  
  1374.   All arguments are optional.  Default is to give short columnar listing,
  1375. sorted alphabetically, using the current directory.  Alphabetizing is case
  1376. insensitive.
  1377.  
  1378.   Patterns may be matched in the given names, using the UNIX-style '*'
  1379. to wildcard any number of characters, and '?' to wildcard a single
  1380. character.  If you need to specify a pathname with spaces in it like
  1381. "Wombat Soup", you need to put quotes around it.  LS can process up to 30
  1382. separate pathname patterns in one command line.
  1383.  
  1384. Bugs:
  1385.  
  1386.   Redirecting the shortlist output to PRT: gives undesirable results,
  1387. since I am using relative cursor positioning commands to format the
  1388. screen output.  I thought about using an array to store a virtual
  1389. screen, but my primary goals were to keep the size down and display
  1390. speed at a maxiumum.  Also, LS cannot pattern match devices (like "dh*:")
  1391. or support multiple levels of pattern matching (like "dh0:?/L*.info").
  1392. This would involve another level of recursion and groking the Device List.
  1393.  
  1394. Changes From 1.0 to 2.0:
  1395.  
  1396.  o Source code prototyped, linted, traced, optimized, tweaked, etc.
  1397.  o Made resident ("pseudo-pure") by linking with cres.o from LC 5.0.
  1398.  o High-volume routines recoded in assembly (lssup.a).
  1399.  o Now handles multiple paths/files on a command line, up to 30.
  1400.  o New sort flags, including no sort.
  1401.  o Enhanced wildcards, understands complex *.?*.* expressions now.
  1402.  o More efficient ExNext() performance, less ram used for recursion.
  1403.  o SIGBREAKF_CTRL_C signal (Ctrl-C) cleanly aborts at any point now.
  1404.  o Command line parser handles quoted pathnames now (LC 5.0 benefit).
  1405.  o Short listing finally auto-adjusts to new console window sizes!
  1406.  o Pen color escape codes bypassed when redirecting long output.
  1407.  o Sorting by size or date is also subsorted alphabetically now.
  1408.  o Long listing shows new 1.3 file attributes, plus comment indicator.
  1409.  o File dates are now in international format, YY-MM-DD.
  1410.  o Fixed listings with files datestamped after 99-12-31 (overflow).
  1411.  o Fixed listings with files datestamped before 78-01-01 (time < 0).
  1412.  
  1413.   Don't send me money for this one!  Its a Christmas present 8-)
  1414. However, if you find any bugs I'd like to hear about them!
  1415.  
  1416.         Justin V. McCormick
  1417.     8330 E. Quincy Ave., C-312
  1418.     Denver, CO 80237
  1419.  
  1420.         Home: 303-290-8429
  1421.     Work: 303-825-4144
  1422. SHAR_EOF
  1423. cat << \SHAR_EOF > ls.h
  1424. #include <dos.h>
  1425. #include <libraries/dosextens.h>
  1426.  
  1427. /*lint -save    */
  1428. /*lint -library */
  1429. #include <proto/exec.h>
  1430. #include <proto/dos.h>
  1431. #include <stdio.h>
  1432. #include <string.h>
  1433. #include <stdlib.h>
  1434. /*lint -restore    */
  1435.  
  1436. extern int tolower (char);
  1437.  
  1438. /* Prevent Lint from complaining about ANSI prototype extensions */
  1439. #ifdef _lint
  1440.  
  1441. #define __asm
  1442. #define __stdargs
  1443. #define R_D0
  1444. #define R_D1
  1445. #define R_A0
  1446.  
  1447. #else
  1448.  
  1449. #define R_D0    register __d0
  1450. #define R_D1    register __d1
  1451. #define R_A0    register __a0
  1452.  
  1453. #endif
  1454.  
  1455. #define MEMF_PUBLIC (1L<<0)
  1456. #define MEMF_CHIP   (1L<<1)
  1457. #define MEMF_FAST   (1L<<2)
  1458. #define MEMF_CLEAR  (1L<<16)
  1459. SHAR_EOF
  1460. cat << \SHAR_EOF > ls.lnk
  1461. DEFINE @_main = @_tinymain
  1462. DEFINE @XCEXIT = xXCEXIT
  1463. DEFINE @chkabort = xchkabort
  1464. DEFINE @write = xchkabort
  1465. DEFINE @_dclose = xchkabort
  1466. SC SD ND
  1467. SHAR_EOF
  1468. cat << \SHAR_EOF > lssup.a
  1469. * --------------------------------------------------------------------- *
  1470. * LSSUP.A    - Assembly support routines for ls.c            *
  1471. * Copyright (c) 1988 by Justin V. McCormick. All Rights Reserved.    *
  1472. * --------------------------------------------------------------------- *
  1473.     IDNT    "lssup.a"
  1474.  
  1475.     include "asm:inc/macros.i"
  1476.  
  1477. ; Assembler options for CAPE
  1478.     BASEREG    B
  1479.     SMALLOBJ
  1480.     OPTIMON
  1481.     ADDSYM
  1482.     DEBUG
  1483.  
  1484. ; Equates
  1485. fib_FileName    equ    $8
  1486. fib_Size    equ    $7C
  1487. fib_NumBlocks    equ    $80
  1488. fib_DateStamp    equ    $84
  1489.  
  1490. ds_Days        equ    $0
  1491. ds_Minute    equ    $4
  1492. ds_Tick        equ    $8
  1493.  
  1494. _LVOAddPort    equ    $FFFFFE9E
  1495. _LVOAllocMem    equ    $FFFFFF3A
  1496. _LVOAllocSignal    equ    $FFFFFEB6
  1497. _LVODebug    equ    $FFFFFF8E
  1498. _LVOFindTask    equ    $FFFFFEDA
  1499. _LVOFreeMem    equ    $FFFFFF2E
  1500. _LVOFreeSignal    equ    $FFFFFEB0
  1501. _LVOGetMsg    equ    $FFFFFE8C
  1502. _LVOPutMsg    equ    $FFFFFE92
  1503. _LVORawDoFmt    equ    $FFFFFDF6
  1504. _LVORemPort    equ    $FFFFFE98
  1505. _LVORead    equ    $FFFFFFD6
  1506. _LVOWaitPort    equ    $FFFFFE80
  1507. _LVOWrite    equ    $FFFFFFD0
  1508.  
  1509. pr_ConsoleTask        EQU    $A4 
  1510. MEMF_CLEAR        EQU    $10000 
  1511. MEMF_PUBLIC        EQU    $1 
  1512. sp_Msg            EQU    $0 
  1513. sp_Pkt            EQU    $14 
  1514. sp_SIZEOF        EQU    $44 
  1515. dp_Link            EQU    $0 
  1516. dp_Port            EQU    $4 
  1517. dp_Arg1            EQU    $14 
  1518. dp_Type            EQU    $8 
  1519. ACTION_SCREEN_MODE    EQU    $3E2 
  1520. LN_NAME            EQU    $A 
  1521. LN_PRI            EQU    $9 
  1522. LN_TYPE            EQU    $8 
  1523. MP_FLAGS        EQU    $E 
  1524. MP_MSGLIST        EQU    $14 
  1525. MP_SIGBIT        EQU    $F 
  1526. MP_SIGTASK        EQU    $10 
  1527. MP_SIZE            EQU    $22 
  1528. NT_MSGPORT        EQU    $4 
  1529. PA_SIGNAL        EQU    $0 
  1530.  
  1531. * External constants
  1532.     XREF    _DOSBase
  1533.     XREF    _Out
  1534.     XREF    _In
  1535. * --------------------------------------------------------------------- *
  1536.     SECTION    lssup,CODE
  1537.  
  1538. * ------------------------------------------------------------------------- *
  1539. * void asprintf(wstr, formatstring, args)
  1540. *   char *wstr;
  1541. *   char *formatstring;
  1542. *   char **args;
  1543. * Synopsis: Given formatstring and args to format, formats output to wstr.
  1544. * Similar to sprintf(), except doesn't handle floats.
  1545. * ------------------------------------------------------------------------- *
  1546.     XDEF    _asprintf
  1547. _asprintf:
  1548.     link    a5,#0
  1549.     movem.l    d0-d2/a0-a3,-(sp)    ;Save everything we might clobber
  1550.  
  1551. * Call format function to convert fmtstring and args to buffer on the stack
  1552.     movea.l    12(a5),a0        ;Grab format string
  1553.     lea    16(a5),a1        ;Grab EA of arguments
  1554.     lea    kput1,a2        ;Grab EA of output subroutine
  1555.     movea.l    8(a5),a3        ;Grab EA of dest workspace
  1556.     SYS    RawDoFmt,4        ;Format it into workspace
  1557.  
  1558.     movem.l    (sp)+,d0-d2/a0-a3    ;Restore registers
  1559.     unlk    a5            ;And stack frame
  1560.     rts
  1561.  
  1562. * ------------------------------------------------------------------------- *
  1563. * RawDoFmt() output routine for xprintf, called for each formatted char.
  1564. * Takes byte in d0 and puts in buffer pointed to by a3, then increments a3.
  1565. * ------------------------------------------------------------------------- *
  1566.     XDEF    kput1
  1567. kput1:
  1568.     move.b    d0,(a3)+
  1569.     rts
  1570.  
  1571. * --------------------------------------------------------------------- *
  1572. * void GetWinBounds(width, height)
  1573. *   long *width, *height;
  1574. *
  1575. * Find current console window, determine width and height
  1576. * in terms of current font, update width and height VPARMS passed.
  1577. * --------------------------------------------------------------------- *
  1578. rpstr    equ    -32
  1579. rpport    equ    -12
  1580. packet    equ    -8
  1581. conid    equ    -4
  1582.  
  1583. width    equ    8
  1584. height    equ    12
  1585.  
  1586.     XDEF    _GetWinBounds
  1587. _GetWinBounds:
  1588.     link    a5,#-32
  1589.     movem.l    d2-d4/a2,-(sp)
  1590.  
  1591.     suba.l    a1,a1
  1592.     SYS    FindTask,4        ;d0 = FindTask(0L), our process
  1593.     movea.l    d0,a0            ;Transfer to address reg
  1594.     move.l    pr_ConsoleTask(a0),conid(a5) ;Save proc->pr_ConsoleTask
  1595.  
  1596.     moveq    #0,d4            ;Clear our success status register
  1597.  
  1598.     moveq    #0,d0
  1599.     movea.l    d0,a0
  1600.     bsr.w    CreatePort
  1601.     move.l    d0,rpport(a5)        ;rpport = CreatePort(0L, 0L)
  1602.      beq.w    gwbdone            ;Oops, no signals or ram available!
  1603.     move.l    #MEMF_PUBLIC+MEMF_CLEAR,d1
  1604.     moveq    #sp_SIZEOF,d0
  1605.     SYS    AllocMem
  1606.     move.l    d0,packet(a5)        ;packet = AllocMem(sizeof(*packet),MEMF_PUBLIC|MEMF_CLEAR)
  1607.      beq.w    gwbfreeport        ;Oops, no ram, free up port
  1608.  
  1609. * Okay, we got our process id, reply port, and packet
  1610. * Now toggle the console into raw mode
  1611.     movea.l    rpport(a5),a2
  1612.     movea.l    d0,a1
  1613.     movea.l    conid(a5),a0
  1614.     moveq    #1,d0
  1615.     bsr.w    SetConsoleType        ;SetConsoleType(1L, conid, packet, rpport)
  1616.  
  1617. * Request a window bounds report
  1618.     moveq    #4,d3
  1619.     lea    gwbrstr(a4),a0
  1620.     move.l    a0,d2
  1621.     move.l    _Out(a4),d1
  1622.     SYS    Write,_DOSBase(a4)    ;Write(Output(), "\2330 q", 4L);
  1623.     cmpi.l    #$0004,d0        ;Did the console choke on it?
  1624.      bne.w    gwbsetcook        ;hmmm, see if we can back out gracefully
  1625.  
  1626. * Read the report string into stack buffer
  1627.     moveq    #16,d3            ;Don't let it get longer than 16 characters
  1628.     lea    rpstr(a5),a0        ;Point to input string area
  1629.     move.l    a0,d2
  1630.     move.l    _In(a4),d1
  1631.     SYS    Read            ;Read(Input(), rpstr, 16L)
  1632.     move.l    d0,d4            ;Save read length while we close shop
  1633.  
  1634. * Turn the console back to cooked mode pronto to avoid cursor blink
  1635. gwbsetcook:
  1636.     movea.l    rpport(a5),a2
  1637.     movea.l    packet(a5),a1
  1638.     movea.l    conid(a5),a0
  1639.     moveq    #0,d0
  1640.     bsr.w    SetConsoleType        ;SetConsoleType(0L, conid, packet, rpport)
  1641.  
  1642. * Release resources we borrowed
  1643. gwbfreepack:
  1644.     move.l    packet(a5),d0        ;Did we allocate a packet?
  1645.      beq.b    gwbfreeport        ;nay, check for port to free
  1646.     movea.l    d0,a1
  1647.     moveq    #sp_SIZEOF,d0
  1648.     SYS    FreeMem            ;Else FreeMem(packet, sizeof(*packet))
  1649.  
  1650. gwbfreeport:
  1651.     move.l    rpport(a5),d0        ;if (rpport)...
  1652.      beq.w    gwbdone            ;nope
  1653.     bsr.w    DeletePort        ;Else DeletePort(rpport)
  1654.  
  1655. * Finally, sanity check window bounds report string
  1656. * d4 = length of report string according to Read()
  1657.     cmpi.l    #9,d4            ;Less than 8 characters returned?
  1658.      ble.w    gwbdone            ;hmmm, phonky bounds report from DOS?
  1659.     lea    rpstr(a5),a2        ;a2 = rpstr
  1660.     cmpi.b    #';',4(a2)        ;Matches a typical report template?
  1661.      bne.w    gwbdone            ;nope, got some weird junk back?
  1662.     cmpi.b    #'r',-1(a2,d4.w)    ;Last byte is 'r' for report?
  1663.      bne.w    gwbdone            ;Nope, message fubar!
  1664.  
  1665. * Parse the height and width variables from the field now
  1666. * Our report format looks like this in hex:
  1667. *     9b 31 3b 31 3b y2 y1 3b x2 x1 20 72
  1668. * Or in ascii:
  1669. *    <0x9b>1;1;20;77 r
  1670. * Which would indicate a width of 77 cols and a height of 20 rows for
  1671. * the current console device
  1672. *
  1673. * REGS:    a2 points to beginning of 'r' terminated string
  1674.  
  1675.     lea    5(a2),a2        ;Point to first char of Y size
  1676.     moveq    #0,d1            ;Clear out work reg
  1677.  
  1678. * Convert ascii rows value to LONG, update host data
  1679.     move.b    (a2)+,d1        ;Grab a Y
  1680.     subi.w    #'0',d1            ;Less ascii offset
  1681.     cmpi.b    #';',(a2)        ;Any more Y digits?
  1682.      beq.b    1$            ;Nope
  1683.     mulu    #10,d1            ;Else shift by 10
  1684.     add.b    (a2)+,d1        ;Add least significant Y digit
  1685.     subi.b    #'0',d1            ;Less ascii offset
  1686.     cmpi.b    #';',(a2)        ;Any more Y digits?
  1687.      beq.b    1$            ;Nope
  1688.     mulu    #$000a,d1        ;Else shift by 10
  1689.     add.b    (a2)+,d1        ;Add least significant Y digit
  1690.     subi.b    #'0',d1            ;Less ascii offset
  1691.                     ;We'll assume screen height < 999 rows    
  1692. 1$
  1693. * Convert ascii columns value to LONG, update host data
  1694.     addq.w    #1,a2            ;Move past the ';' separator
  1695.     moveq    #0,d2            ;Zap work reg
  1696.     move.b    (a2)+,d2        ;Grab msd of X
  1697.     cmpi.b    #' ',d2            ;Premature end?
  1698.      beq.w    gwbdone            ;Huh, must be garbage - don't update VPARMS
  1699.     cmpi.b    #';',d2            ;Also a possible error
  1700.      beq.w    gwbdone
  1701.     cmpi.b    #'r',d2            ;And what about this?
  1702.      beq.w    gwbdone
  1703.  
  1704.     subi.b    #'0',d2            ;Okay, adjust ascii offset
  1705.     cmpi.b    #' ',(a2)        ;Hit end of report?
  1706.      beq.b    2$            ;Yep
  1707.     mulu    #$000a,d2        ;Else shift by 10
  1708.     add.b    (a2)+,d2        ;Add next digit
  1709.     subi.b    #'0',d2            ;Ascii adjust
  1710.     cmpi.b    #' ',(a2)        ;Hit end of report?
  1711.      beq.b    2$            ;Yep
  1712.     mulu    #$000a,d2        ;Else shift by 10
  1713.     add.b    (a2),d2            ;Add next digit
  1714.     subi.b    #'0',d2            ;Ascii adjust
  1715.  
  1716. 2$
  1717. * Finally, update parameters by reference
  1718.     movea.l    height(a5),a0        ;Grab height VPARM
  1719.     move.l    d1,(a0)            ;*height = d1
  1720.     movea.l    width(a5),a0        ;Grab width VPARM
  1721.     move.l    d2,(a0)            ;*width = d2
  1722.  
  1723. gwbdone:
  1724.     movem.l    (sp)+,d2-d4/a2
  1725.     unlk    a5
  1726.     rts
  1727.  
  1728. * --------------------------------------------------------------------- *
  1729. * __asm void SetConsoleType(flag, id, packet, port)
  1730. *   register __d0 long flag;
  1731. *   register __a0 struct Process *id;
  1732. *   register __a1 struct StandardPacket *packet;
  1733. *   register __a2 struct MsgPort *port;
  1734. *
  1735. * Flag = 1L -- Raw mode
  1736. *      = 0L -- Cooked mode
  1737. * --------------------------------------------------------------------- *
  1738.     XDEF    SetConsoleType
  1739. SetConsoleType:
  1740.     movem.l    a3/a5,-(sp)
  1741.  
  1742.     movea.l    a0,a3            ;Copy process pointer
  1743.     movea.l    a1,a5            ;Copy packet pointer
  1744.     lea    sp_Pkt(a5),a0        ;a0 = &packet->sp_Pkt
  1745.     move.l    a0,sp_Msg+LN_NAME(a5)    ;p->sp_Msg.mn_Node.ln_Name = &p->sp_Pkt
  1746.     lea    sp_Msg(a5),a0        ;a0 = &packet->sp_Msg
  1747.     move.l    a0,sp_Pkt+dp_Link(a5)    ;p->sp_Pkt.dp_Link = &p->sp_Msg
  1748.     move.l    a2,sp_Pkt+dp_Port(a5)    ;p->sp_Pkt.dp_Port = replyport
  1749.     move.l    #ACTION_SCREEN_MODE,sp_Pkt+dp_Type(a5)    ;Set function
  1750.  
  1751.     tst.w    d0            ;On or Off?
  1752.      beq.w    1$
  1753.     move.l    #-1,sp_Pkt+dp_Arg1(a5)    ;RAW ON
  1754.     bra.b    2$
  1755. 1$
  1756.     clr.l    sp_Pkt+dp_Arg1(a5)    ;RAW OFF
  1757. 2$
  1758.     movea.l    a3,a0
  1759.     movea.l    a5,a1
  1760.     SYS    PutMsg,4        ;PutMsg(proc, packet)
  1761.  
  1762.     movea.l    a2,a0
  1763.     SYS    WaitPort        ;WaitPort(port)
  1764.     movea.l    a2,a0
  1765.     SYS    GetMsg            ;(void)GetMsg(port)
  1766.  
  1767.     movem.l    (sp)+,a3/a5
  1768.     rts
  1769.  
  1770. * ------------------------------------------------------------------------- *
  1771. * struct MsgPort *CreatePort(name, pri) (a0/d0)
  1772. * ------------------------------------------------------------------------- *
  1773.     XDEF    CreatePort
  1774. CreatePort:
  1775.     movem.l    d5/d7/a2/a5,-(sp)
  1776.  
  1777.     move.l    a0,a5            ;Save Name
  1778.     move.l    d0,d5            ;Save Pri
  1779.  
  1780. * Allocate a free signal, crap out if we can't
  1781.     moveq    #-1,d0
  1782.     SYS    AllocSignal,4
  1783.     cmp.l    #-1,d0            ;Did we get a signal?
  1784.      bne.b    cpgotsig        ;Yep
  1785.     moveq    #0,d0            ;Otherwise return NULL
  1786.     bra.w    cpdone
  1787.  
  1788. cpgotsig:
  1789.     move.l    d0,d7            ;Save our signal
  1790.  
  1791. * Allocate memory for MsgPort
  1792.     moveq.l    #MP_SIZE,d0        ;Size of MsgPort
  1793.     move.l    #MEMF_PUBLIC!MEMF_CLEAR,d1    ;Type of memory
  1794.     SYS    AllocMem        ;Allocate it
  1795.     tst.l    d0            ;Did we get it?
  1796.      bne.b    cpgotport        ;Yep
  1797.  
  1798.     move.l    d7,d0            ;Otherwise crap out, free signal
  1799.     SYS    FreeSignal
  1800.     moveq    #0,d0            ;Return NULL
  1801.     bra.w    cpdone
  1802.  
  1803. cpgotport:
  1804.     move.l    d0,a2            ;This is our new port!
  1805.     move.l    a5,LN_NAME(a2)        ;port->mp_Node.ln_Name = name
  1806.     move.b    d5,LN_PRI(a2)        ;port->mp_Node.ln_Pri = priority
  1807.     move.b    #NT_MSGPORT,LN_TYPE(a2) ;port->mp_Node.ln_Type = NT_MSGPORT
  1808.     move.b    #PA_SIGNAL,MP_FLAGS(a2) ;port->mp_Flags = PA_SIGNAL
  1809.     move.b    d7,MP_SIGBIT(a2)    ;port->mp_SIGBIT = sigBit
  1810.     suba.l    a1,a1
  1811.     SYS    FindTask
  1812.     move.l    d0,MP_SIGTASK(a2)    ;port->mp_SIGTASK = FindTask(0L)
  1813.     
  1814.     cmpa.l    #0,a5            ;Is this a new name?
  1815.     beq.b    cpnoname        ;Nope, add it to the msg list
  1816.  
  1817.     movea.l    a2,a1
  1818.     SYS    AddPort            ;Otherwise add this port
  1819.     move.l    a2,d0            ;Return port pointer
  1820.     bra.b    cpdone
  1821.  
  1822. cpnoname:
  1823. * Initialized New List head
  1824.     lea    MP_MSGLIST(a2),a0     ;a0 = &port->mp_MsgList
  1825.     move.l    a0,(a0)            ;list->lh_Head = list
  1826.     addq.l    #4,(a0)            ;list->lh_Head += 4L
  1827.     clr.l    4(a0)            ;list->lh_Tail = 0L
  1828.     move.l    a0,8(a0)        ;list->lh_TailPred = list
  1829.     move.l    a2,d0            ;Return port pointer
  1830.  
  1831. cpdone:
  1832.     movem.l    (sp)+,d5/d7/a2/a5
  1833.     rts
  1834.  
  1835. * ------------------------------------------------------------------------- *
  1836. * DeletePort(port)(d0)
  1837. * ------------------------------------------------------------------------- *
  1838.     XDEF    DeletePort
  1839. DeletePort:
  1840.     move.l    a5,-(sp)
  1841.  
  1842.     move.l    d0,a5
  1843.     tst.l    LN_NAME(a5)        ;Is there a name?
  1844.     beq.s    dpnoname
  1845.     
  1846.     move.l    d0,a1
  1847.     SYS    RemPort,4        ;RemPort(port)
  1848.     
  1849. dpnoname:
  1850.     move.b    #$ff,LN_TYPE(a5)     ;port->mp_Node.ln_Type = 0xff
  1851.     move.l    #-1,MP_MSGLIST(a5)     ;port->mp_MsgList.lh_Head = -1L
  1852.  
  1853.     moveq    #0,d0
  1854.     move.b    MP_SIGBIT(a5),d0    ;d0 = port->mp_SigBit
  1855.     SYS    FreeSignal,4        ;FreeSignal(d0)
  1856.  
  1857.     moveq    #MP_SIZE,d0
  1858.     move.l    a5,a1
  1859.     SYS    FreeMem            ;FreeMem(port, sizeof(*port))
  1860.  
  1861.     move.l    (sp)+,a5
  1862.     rts
  1863.  
  1864. * ------------------------------------------------------------------------
  1865. * char *FibFileDate(fib_date)
  1866. *   register struct DateStamp *fib_date;
  1867. *
  1868. *   Calculate date based on DateStamp structure and return a pointer
  1869. * to the formatted date string.
  1870. * ------------------------------------------------------------------------
  1871.     XDEF    _FibFileDate
  1872. _FibFileDate:
  1873.     link    a5,#0
  1874.     movem.l    d3-d7/a4,-(sp)
  1875.  
  1876.     movea.l    8(a5),a1        ;Grab datestamp pointer
  1877.     moveq    #78,d7            ;Initial year = 1978
  1878.  
  1879.     move.l    (a1),d5            ;days = fib_date->ds_Days
  1880.      blt    ffdbaddate        ;Hey! you can't be negative! Invalid date...
  1881.  
  1882. * Determine what year it is
  1883.     divu    #1461,d5
  1884.     move.l    d5,d0            ;Stash it
  1885.     ext.l    d5
  1886.     lsl.l    #2,d5
  1887.     add.l    d5,d7            ;year += (days / 1461) * 4
  1888.  
  1889. * Count how many months into that year
  1890. ffdgetmo:
  1891.     swap    d0            ;days %= 1461
  1892.     move.w    d0,d5
  1893.  
  1894. 1$    tst.w    d5            ;Out of days yet?
  1895.      beq    3$            ;Yep, done here
  1896.  
  1897.     move.w    #365,d6            ;Else month_days = 365
  1898.     move.w    d7,d0            ;Grab year
  1899.     andi.w    #3,d0            ;if (year & 3) == 0 Leap year?
  1900.      bne    2$            ;Nope
  1901.     addq.w    #1,d6            ;Otherwise bump month_days
  1902.  
  1903. 2$    cmp.w    d6,d5            ;is day < month_days?
  1904.      blt    3$            ;yep, done here
  1905.     sub.w    d6,d5            ;otherwise day -= month_days
  1906.  
  1907.     addq.l    #1,d7            ; year++
  1908.     bra    1$
  1909. 3$
  1910.  
  1911. * Count how many days into that month of that year
  1912. ffdgetday:
  1913. ;for (i = 0, day++; i < 12; i++)
  1914.     moveq    #0,d4            ;current month = 0
  1915.     moveq    #0,d6            ;Zap hinybs
  1916.     addq.w    #1,d5
  1917.     lea    _dayspermonth(a4),a0
  1918.  
  1919. 1$
  1920.     move.b    0(a0,d4.w),d6        ;month_days = dayspermonth[i]
  1921.  
  1922.     cmpi.w    #1,d4            ;if (i == 1 && (year & 3) == 0)
  1923.      bne    2$
  1924.     move.w    d7,d0
  1925.     andi.w    #3,d0
  1926.      bne    2$
  1927.     addq.w    #1,d6            ;month_days++
  1928.  
  1929. 2$    cmp.w    d6,d5            ;if (day <= month_days)
  1930.      ble    4$            ;Break out, found the right month
  1931.  
  1932.     sub.w    d6,d5            ;Else, day -= month_days
  1933.  
  1934.     addq.w    #1,d4            ;i++
  1935. 3$    cmpi.w    #12,d4            ;Done all months yet?
  1936.      blt    1$            ;Nope
  1937.  
  1938. 4$
  1939. ffdprint:
  1940. 1$    cmpi.l    #99,d7            ;while (year >= 100)
  1941.      ble    2$
  1942.     subi.l    #100,d7            ;year -= 100
  1943.     bra    1$
  1944. 2$
  1945. ;asprintf(datestr, "%02d-%02d-%02d %02d:%02d:%02d", i + 1, day, year, hour, min, sec)
  1946.     move.l    8(a1),d0        ;sec = fib_date->ds_Tick / 50;
  1947.     divu    #50,d0
  1948.     move.w    d0,-(sp)        ;Push secs
  1949.  
  1950.     moveq    #0,d0            ;Zap reg
  1951.     move.w    6(a1),d0        ;min = fib_date->ds_Minute
  1952.     move.w    d0,d1            ;Clone it
  1953.     divu    #60,d0
  1954.     move.w    d0,d3            ;hour = min / 60
  1955.     mulu    #60,d0
  1956.     sub.w    d0,d1            ;min -= hour * 60
  1957.     move.w    d1,-(sp)        ;Push mins
  1958.  
  1959.     move.w    d3,-(sp)        ;Push hours
  1960.     addq.w    #1,d4            ;Push day of month (offset by 1!)
  1961.     move.w    d5,-(sp)        ;Push month
  1962.     move.w    d4,-(sp)
  1963.     move.w    d7,-(sp)        ;Push year
  1964.     pea    _datepat(a4)        ;Push the format pattern
  1965.     pea    _datestr(a4)        ;Push destination buffer
  1966.     jsr    _asprintf    
  1967.     lea    20(sp),sp
  1968.     lea    _datestr(a4),a0
  1969.     move.l    a0,d0            ;return((char *)&datestr[0])
  1970.  
  1971. ffddone:
  1972.     movem.l    (sp)+,d3-d7/a4
  1973.     unlk    a5
  1974.     rts
  1975.  
  1976. ffdbaddate:
  1977.     lea    _baddatestr(a4),a0    ;return (" <Invalid Date> ");
  1978.     move.l    a0,d0
  1979.     bra    ffddone
  1980.  
  1981. *----------------------------------------------------------------------
  1982. * LONG iswild(name)
  1983. *   char *name;
  1984. *
  1985. * Search a string for wild characters, return 1 if found
  1986. *----------------------------------------------------------------------
  1987.     XDEF    _iswild
  1988. _iswild:
  1989.     movea.l    4(sp),a0        ;Grab string pointer
  1990.     moveq    #0,d0            ;Clear out our character register
  1991. ischk1:
  1992.     move.b    (a0)+,d0        ;Grab a char
  1993.      beq    iwdone            ;Might be end of string?
  1994.     cmpi.b    #'*',d0            ;Is it *?
  1995.      beq    iswdone            ;yep, is wild
  1996.     cmpi.b    #'?',d0            ;Is it a qmark
  1997.      bne    ischk1            ;Nope, check next character
  1998.  
  1999. iswdone:
  2000.     moveq    #1,d0
  2001. iwdone:
  2002.     rts
  2003.  
  2004.  
  2005. * ------------------------------------------------------------------------
  2006. ; Compare a wild card name with a normal name
  2007. ; LONG wildmatch (name, wild)
  2008. ;   char *name, *wild;
  2009. * ------------------------------------------------------------------------
  2010.     XDEF    _wildmatch
  2011. _wildmatch:
  2012.     link    a5,#-64
  2013.     movem.l    d3/a2-a3,-(sp)
  2014.  
  2015.     movea.l    8(a5),a2        ;Grab name
  2016.     movea.l    12(a5),a3        ;Grab pattern
  2017.     lea    -64(a5),a0        ;back[0][0]
  2018.     lea    -60(a5),a1        ;back[0][1]
  2019.  
  2020.     moveq    #0,d3            ;bi = 0
  2021.  
  2022. wmloop1:
  2023.     tst.b    (a2)            ;End of name?
  2024.      bne    wmnoteon
  2025.     tst.b    (a3)            ;End of pattern?
  2026.      beq    wmmatched        ;Yep, we matched
  2027.  
  2028. wmnoteon:
  2029.     cmpi.b    #'*',(a3)        ;Is it a splat?
  2030.      bne    wmnotstar        ;Nope, maybe '?'
  2031.  
  2032.     cmpi.w    #64,d3            ;Have we hit max expression depth?
  2033.      beq    wmnomatch        ;Yep, ran out of room in recursion table
  2034.  
  2035. ;back[bi][0] = w
  2036.     move.l    a3,0(a0,d3.w)        ;Stash pointer to this '*' in table
  2037.  
  2038. ;back[bi][1] = n
  2039.     move.l    a2,0(a1,d3.w)
  2040.  
  2041.     addq.w    #8,d3            ;++bi
  2042.     addq.w    #1,a3            ;++w
  2043.     bra    wmloop1            ;Check next
  2044.  
  2045. wmgoback:
  2046.     subq.w    #8,d3            ;--bi
  2047.     move.l    a0,d0
  2048. wmback1:
  2049.     tst.w    d3            ;while (bi >= 0 && *back[bi][1] == '\x0')
  2050.      blt    wmbacked
  2051.     movea.l    0(a1,d3.l),a0
  2052.     tst.b    (a0)
  2053.      bne    wmbacked
  2054.  
  2055.     subq.w    #8,d3            ;--bi
  2056.     bra    wmback1
  2057.  
  2058. wmbacked:
  2059.     tst.w    d3            ;if (bi < 0)
  2060.      blt    wmnomatch        ;return (0)
  2061.  
  2062.     movea.l    d0,a0
  2063.     movea.l    0(a0,d3.w),a3        ;w = back[bi][0] + 1
  2064.     addq.w    #1,a3    
  2065.  
  2066.     addq.l    #1,0(a1,d3.w)
  2067.     movea.l    0(a1,d3.l),a2        ;n = ++back[bi][1]
  2068.  
  2069.     addq.w    #8,d3            ;++bi
  2070.     bra    wmloop1
  2071.  
  2072. wmnotstar:
  2073.     cmpi.b    #'?',(a3)        ;Is it '?'
  2074.      bne    wmnotqmark
  2075.  
  2076.     tst.b    (a2)            ;Reached end of string?
  2077.      bne    wmincpoint        ;Nope, move on to next char
  2078.  
  2079.     tst.w    d3            ;Are we at top level of expression?
  2080.      beq    wmnomatch        ;Yep, expression didn't match
  2081.     bra    wmgoback        ;Otherwise pop a level and try to match
  2082.  
  2083. wmnotqmark:
  2084.     move.b    (a2),d0            ;Grab a char from bstr
  2085.     cmpi.b    #$40,d0            ;less than @ character?
  2086.      bls    1$            ;Yep
  2087.     cmpi.b    #$5a,d0            ;Greater than Z?
  2088.      bhi    1$            ;Yep
  2089.     addi.b    #$20,d0
  2090. 1$
  2091.     move.b    (a3),d1            ;Grab a char from bstr
  2092.     cmpi.b    #$40,d1            ;less than @ character?
  2093.      bls    2$            ;Yep
  2094.     cmpi.b    #$5a,d1            ;Greater than Z?
  2095.      bhi    2$            ;Yep
  2096.     addi.b    #$20,d1
  2097. 2$
  2098.     cmp.b    d0,d1            ;*n = *w?
  2099.      beq    wmincpoint        ;Yep, move on past
  2100.  
  2101.     tst.w    d3            ;Are we at top expression level?
  2102.      beq    wmnomatch        ;Yep, they didn't match
  2103.     bra    wmgoback        ;Nope, process next part
  2104.  
  2105. wmincpoint:
  2106.     tst.b    (a2)            ;Done with name?
  2107.      beq    wmnamend        ;Yep
  2108.     addq.w    #1,a2            ;Otherwise increment name pointer
  2109.  
  2110. wmnamend:
  2111.     tst.b    (a3)            ;End of pattern?
  2112.      beq    wmmatched        ;Yep, we matched
  2113.     addq.w    #1,a3            ;Otherwise inc wild pointer, match next char
  2114.     bra    wmloop1
  2115.  
  2116. wmmatched:
  2117.     moveq    #1,d0
  2118.     bra    wmdone
  2119.  
  2120. wmnomatch:
  2121.     moveq    #0,d0
  2122.  
  2123. wmdone:
  2124.     movem.l    (sp)+,d3/a2-a3
  2125.     unlk    a5
  2126.     rts
  2127.  
  2128. * --------------------------------------------------------------------- *
  2129. * void SortFibs (keytype, direction, fibheadp)
  2130. *           d0       d1         a0
  2131. *  int keytype;
  2132. *  struct FibEntry *fibheadp;
  2133. *
  2134. * Selection sort a linked list of FibEntrys based on a keycode         *
  2135. * --------------------------------------------------------------------- *
  2136.     XDEF    _SortFibs
  2137. _SortFibs:
  2138.     link    a5,#0
  2139.     movem.l    d2-d4/a2-a3/a6,-(sp)
  2140.  
  2141.     move.l    d0,d2            ;Save keytype
  2142.     move.l    d1,d4            ;Save direction of sort
  2143.     movea.l    a0,d3            ;Save fibheadp
  2144.     movea.l    a0,a2            ;a2 = a0 = i
  2145.  
  2146. sfILoop:
  2147.     cmp.l    (a2),d3            ;i->NextFib != fibheadp?
  2148.      beq    sfdone            ;Nope, wrapped around to start
  2149.     movea.l    a2,a6            ;k = i
  2150.     movea.l    (a2),a3            ;j = i->NextFib
  2151.  
  2152. sfJLoop:
  2153.     cmp.l    a3,d3            ;j != fibheadp?
  2154.      beq    sfJdone            ;Nope compared them all, see if swapped any
  2155.     movea.l    8(a6),a0        ;a0 = k->Fibp
  2156.     movea.l    8(a3),a1        ;a1 = j->Fibp
  2157.     move.l    d2,d0            ;d0 = keytype
  2158.     jsr    _CompFibs        ;d0 = CompFibs(keytype, k->Fibp, j->Fibp)
  2159.     tst.w    d4            ;Reverse sort?
  2160.      beq    1$            ;Nope
  2161.     bchg.l    #0,d0            ;Else reverse sense of return
  2162. 1$
  2163.     tst.l    d0            ;Return != 0?
  2164.      beq    sfNextJ            ;Nope, these two are in order
  2165.     movea.l    a3,a6            ;else k = j, this is new swap
  2166.  
  2167. sfNextJ:
  2168.     movea.l    (a3),a3            ;j = j->NextFib
  2169.     bra    sfJLoop            ;Check bounds
  2170.  
  2171. sfJdone:
  2172.     cmpa.l    a6,a2            ;k != i, did we swap?
  2173.      beq    sfNextI            ;Nope, i was in correct position already
  2174.     move.l    8(a6),d0
  2175.     move.l    8(a2),8(a6)
  2176.     move.l    d0,8(a2)        ;Else SwapFibs (k, i)
  2177.  
  2178. sfNextI:
  2179.     movea.l    (a2),a2            ;i = i->NextFib
  2180.     bra    sfILoop            ;Check bounds
  2181.  
  2182. sfdone:
  2183.     movem.l    (sp)+,d2-d4/a2-a3/a6
  2184.     unlk    a5
  2185.     rts
  2186.  
  2187. * --------------------------------------------------------------------- *
  2188. * int CompFibs (keytype, a, b)
  2189. *         d0     a0 a1
  2190. *   int keytype;
  2191. *   struct FileInfoBlock *a, *b;
  2192. *
  2193. * Used by SortFibs to determine precedence of Fibs.
  2194. * --------------------------------------------------------------------- *
  2195.     XDEF    _CompFibs
  2196. _CompFibs:
  2197.     tst.w    d0            ;Alphabetize?
  2198.      bne    cfnalpha        ;Nope
  2199.  
  2200. * Compare lexigraphically, ignoring case differences
  2201. cfalpha:
  2202.     lea    fib_FileName(a0),a0    ;a = &Fipb->fib_FileName
  2203.     lea    fib_FileName(a1),a1    ;b = &Fipb->fib_FileName
  2204.  
  2205. ;  for(; *a && tolower(*a) == tolower(*b); a++, b++);
  2206. lccstart:
  2207.     tst.b    (a0)            ;Is there a char here at source?
  2208.      beq    lcceostr        ;Nope, fell off the end
  2209.  
  2210.     move.b    (a1)+,d1        ;Grab a char from bstr
  2211.     cmpi.b    #$40,d1            ;less than @ character?
  2212.      bls    1$            ;Yep
  2213.     cmpi.b    #$5a,d1            ;Greater than Z?
  2214.      bhi    1$            ;Yep
  2215.     addi.b    #$20,d1
  2216. 1$
  2217.     move.b    (a0)+,d0        ;Grab a char from astr
  2218.     cmpi.b    #$40,d0            ;less than @ character?
  2219.      bls    2$            ;Yep
  2220.     cmpi.b    #$5a,d0            ;Greater than Z?
  2221.      bhi    2$            ;Yep
  2222.     addi.b    #$20,d0
  2223. 2$
  2224.     cmp.b    d0,d1            ;are they the same?
  2225.      beq    lccstart        ;Yep, compare next pair of chars
  2226.  
  2227. lcceostr:
  2228.     sub.b    d1,d0            ;return(tolower(*astr) - tolower(*bstr))
  2229.      bgt    cftrue            ; > 0?, return TRUE
  2230.     bra    cffalse            ;Else return FALSE
  2231.  
  2232. cfnalpha:
  2233.     subq.w    #1,d0            ;Size?
  2234.      bne    cfnsize            ;Nope
  2235.  
  2236. * Compare fib_Sizes
  2237.     move.l    fib_Size(a1),d0        ;d0 = bfib->fib_Size
  2238.     cmp.l    fib_Size(a0),d0        ;a->fib_Size > b->fib_Size?
  2239.      blt    cftrue            ;Yep, return TRUE
  2240.      bgt    cffalse            ;<, return FALSE
  2241.     bra    cfalpha            ;Else it's a tie, alphabetize
  2242.  
  2243. cfnsize:
  2244.     subq.w    #1,d0            ;Time?
  2245.      bne    cffalse            ;Nope, an error!
  2246.  
  2247. * Compare fib_DateStamps
  2248.     lea    fib_DateStamp(a0),a0    ;a = &afib->fib_DateStamp
  2249.     lea    fib_DateStamp(a1),a1    ;b = &bfib->fib_DateStamp
  2250.     move.l    ds_Days(a1),d0        ;d0 = bdate->ds_Days
  2251.     cmp.l    ds_Days(a0),d0        ;a->ds_Days > b->ds_Days?
  2252.      blt    cftrue            ;Yep, a is older
  2253.      bgt    cffalse            ;Else if a < b, b is older
  2254.                     ;Else they are the same day, check min/tick
  2255.     move.l    ds_Minute(a0),d0
  2256.     sub.l    ds_Minute(a1),d0    ;d0 = amin - bmin
  2257.     muls    #3000,d0        ;     * 3000
  2258.     add.l    ds_Tick(a0),d0
  2259.     sub.l    ds_Tick(a1),d0        ;     + atick - btick
  2260.      bgt    cftrue            ;Hey, a > b
  2261.      blt    cffalse            ;a < b, return false
  2262.     bra    cfalpha            ;Else its the same date, alphabetize
  2263.  
  2264. cftrue:
  2265.     moveq    #1,d0
  2266.     rts
  2267. cffalse:
  2268.     moveq    #0,d0
  2269.     rts
  2270.  
  2271. * --------------------------------------------------------------------- *
  2272.     SECTION    __MERGED,DATA
  2273. * --------------------------------------------------------------------- *
  2274.     XDEF    _dayspermonth
  2275. _dayspermonth:
  2276.     dc.b    31,28,31,30,31,30,31,31,30,31,30,31
  2277.     XDEF    _datepat
  2278. _datepat:
  2279.     dc.b    '%02d-%02d-%02d %02d:%02d:%02d',0
  2280.     CNOP    0,2
  2281.     XDEF    _baddatestr
  2282. _baddatestr:
  2283.     dc.b    '00-00-00 00:00:00',0
  2284.     CNOP    0,2
  2285.  
  2286.     XDEF    gwbrstr
  2287. gwbrstr:
  2288.     dc.b    $9b,'0 q'
  2289.  
  2290. * --------------------------------------------------------------------- *
  2291.     SECTION    __MERGED,BSS
  2292. * --------------------------------------------------------------------- *
  2293.     XDEF    _datestr
  2294. _datestr:
  2295.     ds.b    40
  2296.  
  2297. * --------------------------------------------------------------------- *
  2298.     END
  2299. * --------------------------------------------------------------------- *
  2300. SHAR_EOF
  2301. cat << \SHAR_EOF > makefile
  2302. # -------------------------------------------
  2303. # LS 2.0 lmkfile by Justin V. McCormick 88/11/27
  2304. # -------------------------------------------
  2305. OBJS    = c2.o ls.o lssup.o
  2306. DEST    = lcs
  2307.  
  2308. ASM    = casm
  2309. ASFLAGS = -cv
  2310. AINC    = ainc:
  2311. LC    = lc
  2312. LC1    = lc1
  2313. LC2    = lc2
  2314. LCFLAGS = -ccfmsu -d2 -rr -v
  2315.  
  2316. LINK    = blink
  2317. LIBS     = lib:lcr.lib
  2318. LNMAP    = F H L S X PLAIN WIDTH 84 HEIGHT 0 SWIDTH 20
  2319.  
  2320. .a.o:
  2321.     $(ASM) -a $*.a -o$*.o -i$(AINC) $(ASFLAGS)
  2322.  
  2323. .c.o:
  2324.     $(LC) $(LCFLAGS) $*
  2325.  
  2326. $(DEST): $(OBJS)
  2327.     $(LINK) $(OBJS) to $(DEST) LIB $(LIBS) WITH $(DEST).lnk MAP $(DEST).map $(LNMAP)
  2328. SHAR_EOF
  2329. #    End of shell archive
  2330. exit 0
  2331. -- 
  2332. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  2333. Have five nice days.
  2334.